package com.alvin.demo.utils;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

public class Tcp_Utils_Mina {

    private final int retryTimes;

    // 默认构造方法为重试次数为3
    public Tcp_Utils_Mina() {
        this.retryTimes = 3;
    }

    // 自定义重试次数
    public Tcp_Utils_Mina(int retryTimes) {
        if (retryTimes > 0 && retryTimes <= 5) {
            this.retryTimes = retryTimes;
        } else {
            this.retryTimes = 3;
        }
    }

    public String sendMessage(String ip, int port, String msg, int timeout) {
        IoConnector connector;
        IoSession session;
        connector = new NioSocketConnector();
        MinaResult minaResult = new MinaResult();
        connector.setHandler(minaResult);
        connector.setConnectTimeoutMillis(500);
        ConnectFuture connFuture = connector.connect(new InetSocketAddress(ip, port));
        connFuture.awaitUninterruptibly();
        try {
            session = connFuture.getSession();
            byte[] bytes = genRequestBytes(msg);
            IoBuffer buffer = IoBuffer.allocate(20);
            // 自动扩容
            buffer.setAutoExpand(true);
            // 自动收缩
            buffer.setAutoShrink(true);
            for (int i = 0; i < retryTimes; i++) {
                buffer.put(bytes);
                buffer.flip();
                session.write(buffer);
                TimeUnit.MILLISECONDS.sleep(timeout);
                if (!minaResult.getContent().isEmpty()) {
                    break;
                }
            }
            session.closeNow();
            connector.dispose();
        } catch (Exception e) {
            minaResult.setContent("error");
        }

        return minaResult.getContent();
    }

    private byte [] genRequestBytes(String msg){
        byte[] bytes = hexString2Bytes(msg);//查询命令
        String[] ss = stringPolishing(getCRC(bytes));
        msg = msg + " " + ss[0] + " "  + ss[1];
        bytes = hexString2Bytes(msg);//再次获取带CRC是byte
        return bytes ;
    }

    /**
     * 给16进制的字符串4位 不足补0
     * @param str  原始字符串
     * @return 返回bits长度的字符串
     */
    private static String[] stringPolishing(String str) {
        int len = str.length();
        StringBuilder StrBuilder = new StringBuilder(str);
        for (int i = 0; i < 4 - len; i++) {
            StrBuilder.insert(0, "0");
        }
        String[] ss = new String[2];
        ss[0] = StrBuilder.substring(0, 2); // CRC 高位
        ss[1] = StrBuilder.substring(2); // CRC 低位
        return ss;
    }

    /**
     * 根据byte数组获取相应的 CRC 检验字符串
     * @param bytes 原始字符串的byte数组
     * @return 返回 CRC 检验字符串
     */
    private static String getCRC(byte[] bytes) {
        int CRC = 0x0000ffff;
        int POLYNOMIAL = 0x0000a001;

        for (byte b : bytes) {
            CRC ^= ((int) b & 0x000000ff);
            for (int j = 0; j < 8; j++) {
                if ((CRC & 0x00000001) != 0) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        return Integer.toHexString(CRC);
    }

    /**
     * 从十六进制字符串到字节数组转换
     *
     * @param hexStr 十六进制字符串
     * @return 字节数组
     */
    private static byte[] hexString2Bytes(String hexStr) {
        hexStr = hexStr.replace(" ", "");
        byte[] b = new byte[hexStr.length() / 2];
        int j = 0;
        for (int i = 0; i < b.length; i++) {
            char c0 = hexStr.charAt(j);
            char c1 = hexStr.charAt(j+1);
            j += 2;
            b[i] = (byte) Integer.parseInt(c0 + "" + c1, 16); // 将16进制字符串转 byte
        }
        return b;
    }

    public static void main(String[] args) {
        int count = 0 ;
        String hexString = NumberUtils.longToHexString(179218272L, 8);
        String command = hexString + " " + "03 FF 11" ;
        Tcp_Utils_Mina tcp_utils = new Tcp_Utils_Mina();
        for (int i = 0 ; i < 100 ; i++ ){
            long currentTimeMillis = System.currentTimeMillis();
            String result = tcp_utils.sendMessage("192.168.0.102", 8886, command, 300);
            System.out.println(result);
            if (result.length()!=71){
                count++ ;
            }
            System.out.println("====================================="+ (System.currentTimeMillis() - currentTimeMillis )+"====================================");
        }
        System.out.println(count);
    }
}
